-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
compute-shader mesh generation example #22296
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
compute-shader mesh generation example #22296
Conversation
|
The generated |
| // Add the compute node as a top-level node to the render graph. This means it will only execute | ||
| // once per frame. Normally, adding a node would use the `RenderGraphApp::add_render_graph_node` | ||
| // method, but it does not allow adding as a top-level node. | ||
| render_graph.add_node(ComputeNodeLabel, ComputeNode::default()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't remember if this is right or not. I think you may need to order it vs the camera driver node?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added the edge here, but I'm really not sure it has any impact here (but it definitely doesn't hurt). Figuring out what the render graph actually looks like could use some debug visualization. Maybe a function that returns a dotviz 🤔
tychedelia
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks!! i've wanted this for a while but only hesitated because i've felt like it should be easier. i have some ideas though and so think this will be a good target to improve things incrementally here
| render_app | ||
| .world_mut() | ||
| .resource_mut::<MeshAllocator>() | ||
| .extra_buffer_usages = BufferUsages::STORAGE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels a bit off. It seems weird that this needs to modify a global resource. But I'm not sure if there's a better alternative
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels like something that should be configurable by inserting a Resource or similar rather than modifying this one, yeah. but the MeshAllocator fields are private and initializing it using its FromWorld implementation is probably more awkward (it also has ordering requirements with other resources).
I also wanted to pass the BufferSlice to the shader instead of the full buffer, but I don't think the offsets match the storage buffer offset requirements (it was 256 on my machine and the offsets I saw were numbers like 4, 12, etc)
|
Could you make it so the cubes aren't clipping the circle mesh? I don't generally comment about the look of examples but it looks a bit strange in it's current state. Also, if I'm reading this correctly this ends up modifying the mesh every frame right? I'd suggest maybe changing the scale every frame to show this is happening. |
Updated.
I instead updated this so that it will only generate once per unique |
|
A bug was found where the program was not waiting for the pipeline to be ready, and on windows/linux vulkan the pipeline takes a few frames to be ready (on macos it is ready immediately). The program now waits before attempting to send applicable meshes and considering them "processed". |
|
|
||
| mesh.asset_usage = RenderAssetUsages::RENDER_WORLD; | ||
| mesh | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kinda sucks that you still have to transmit this once, would be nice if there was a way to just tell it to zero out a region for you. Not really related to this PR though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You do in this example which is intended to be the "simplest" approach to getting a compute-shader powered mesh into the "regular" bevy handling just like every other mesh, not the most performant. This example only requires setting up: the mesh data you expect to write and the compute shader to write it, and in return you get compatibility with all the things that use Mesh3d in their queries (lights, etc).
Obviously if you skip bevy's handling, opt-out of standardmaterial, and go compute-shader -> custom vertex/fragment pipeline, or use mesh shaders in the future, then everything can stay on the gpu and passing meshes around becomes significantly simpler (custom phase item example is a good showcase of that). Maybe there's another compute shader example to make there.
The there's a bunch of examples in the shader_advanced section that show off how to do various things, but bevy's built-in draw commands for specializedmeshpipeline, etc assume meshallocator participation and extraction assumes mesh3d.
| name = "Compute Shader Mesh" | ||
| description = "A compute shader that generates a mesh that is controlled by a Handle" | ||
| category = "Shaders" | ||
| wasm = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would this one work in WebGPU but not WebGL2?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, it can't work in webgl2 because it uses a compute shader which isn't available in webgl2.
|
Very nice addition, I was trying to figure this out earlier myself but this lays it out pretty clearly :) |
# Objective People have been asking how to get a compute shader-built mesh into bevy's "stuff". Some people want to control the lifetime of the mesh via Handle, and others don't don't how to set data in bind groups. ## Solution a new example that shows how to initialize a mesh handle with a render_world usage mesh, and then put the output of the compute shader into the mesh_allocator slab for the mesh. The demo creates a scene with a camera, light, a circular base mesh, and an empty "cube to be" mesh that is shared by cloning the handle across two entities. The compute shader then fills in the data directly into the mesh_allocator slabs for the vertex/index buffers. If the compute shader failed, there would be no cube meshes showing as the data would be empty. ## Testing ``` cargo run --example compute_mesh ``` --- ## Showcase <img width="3392" height="2106" alt="screenshot-2025-12-29-at-16 06 48@2x" src="https://github.com/user-attachments/assets/88d8fed4-e3c1-418e-bb04-6f08d673403a" />
|
linux failure seems unrelated to the PR |
Objective
People have been asking how to get a compute shader-built mesh into bevy's "stuff".
Some people want to control the lifetime of the mesh via Handle, and others don't don't how to set data in bind groups.
Solution
a new example that shows how to initialize a mesh handle with a render_world usage mesh, and then put the output of the compute shader into the mesh_allocator slab for the mesh.
The demo creates a scene with a camera, light, a circular base mesh, and an empty "cube to be" mesh that is shared by cloning the handle across two entities. The compute shader then fills in the data directly into the mesh_allocator slabs for the vertex/index buffers.
If the compute shader failed, there would be no cube meshes showing as the data would be empty.
Testing
Showcase